﻿/*
 * DATAFLOWCORE
 * 
Copyright 2012 - Mindstorm Multitouch Limited

Author - Bertrand Nouvel

DataFlowCore is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

DataFlowCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.
*/


using MongoDB.Bson;
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Linq;



namespace DFlowCore
{
    public partial class Engine : MarshalByRefObject
    {


        #region PROTOCOL_RELATED_COMMANDS



        [AutoServerCommand("kill")]
        public string Kill()
        {
            continueMainLoop = false;
            return "ok";
        }

        [AutoServerCommand("nop", "noop")]
        public string NoOp()
        {
            return "ok";
        }

        [AutoServerCommand("get_datastream_address")]
        public string GetDatastreamAddress()
        {
            return variables["vogon_datastream_address"] as string;
        }

        [AutoServerCommand("commands_list")]
        public List<string> ListCommands()
        {

            System.Collections.Generic.List<string> commandnames = new System.Collections.Generic.List<string>();
            Type t = typeof(Engine);
            foreach (System.Reflection.MethodInfo mi in t.GetMethods())
            {
                foreach (object a in mi.GetCustomAttributes(typeof(ServerCommand), false))
                {
                    commandnames.Add(mi.Name);
                    foreach (string al in (a as ServerCommand).aliases)
                    {
                        commandnames.Add(al);
                    }
                }
            }

            return commandnames;
        }




        #endregion


        #region SERVER_DATA_COMMANDS
        [AutoServerCommand("vogon_server_list_files")]
        public IEnumerable<string> ServerListFiles(string extension = "*.jsonc")
        {
            if (!extension.StartsWith("*.")) { extension = "*." + extension; }
            return System.IO.Directory.EnumerateFiles(System.IO.Directory.GetCurrentDirectory(), extension, SearchOption.AllDirectories);
        }





        [ServerCommand("variables_dump")]
        public BsonDocument DumpVariables(BsonDocument q)
        {
            BsonDocument bd = new BsonDocument();
            foreach (string k in variables.Keys)
            {
                if (variables[k] as string != null)
                    bd.Add(k, variables[k

                        ] as string);
            }
            return bd;
        }
        #endregion







        #region PLUGIN_RELATED_COMMANDS
        [AutoServerCommand("plugins_list")]
        public IEnumerable<string> PluginList(bool refresh=false)
        {
            if (refresh)
            {
                this.DoLookForPlugins();
            }
            return known_plugins;
        }


        /// <summary>
        /// Same as plugin load, except that it would 
        /// also do the look up to find the plugin.
        /// </summary>
        /// <param name="asmname"> part of the assembly name to be found matched</param>
        /// <returns></returns>
        [AutoServerCommand("plugin_find_and_load")]
        public bool PluginFindAndLoad(string asmname)
        {
            string asml = asmname.ToLower();
            if (loaded_assemblies_time.Keys.Where(x=>(System.IO.Path.GetFileName(x).ToLower().Contains(asml))).Count()>0)
            {
                return true;
            }

            if (known_plugins.Where(x => (System.IO.Path.GetFileName(x).ToLower().Contains(asml))).Count() > 0)
            {
                string asmpath = known_plugins.Where(
                   x => (System.IO.Path.GetFileName(x)
                        .ToLower().Contains(asml))).First();
                return (PluginLoad(asmname) == "ok");
            }

            return false;
        }


        [AutoServerCommand("plugin_load")]
        public string PluginLoad(string asmpath)
        {
            DFlowCore.Log.Info("Loading plugin " + asmpath);
            if (loaded_assemblies_time.ContainsKey(asmpath))
            {
                return "ok";
            }
            System.Reflection.Assembly asm = null;


            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);

            assemblyAdditionalLoadPath = System.IO.Path.GetDirectoryName(asmpath);
            if (assemblyAdditionalLoadPath.Length == 0) assemblyAdditionalLoadPath = null;
            FileInfo fi = new FileInfo(asmpath);

            byte[] b = new byte[fi.Length];
            using (FileStream fs = new FileStream(asmpath, FileMode.Open, FileAccess.Read))
            {
                fs.Read(b, 0, (int)fi.Length);
            }

            
            string ts = System.IO.Path.GetFileName(AssemblyUpload(asmpath, b));
            ts = ts.Substring(0, ts.Length - 4);
            try
            {
                asm = appdomain.Load(ts);
            }
            catch (System.Exception e)
            {
                DFlowCore.Log.Error(e.ToString());
                return "not ok";
            }
            
            currentDomain.AssemblyResolve -= new ResolveEventHandler(LoadFromSameFolder);
            loaded_assemblies.Add(asm);
            loaded_assemblies_time.Add(asmpath, System.IO.File.GetLastAccessTimeUtc(asmpath));
            return "ok";
        }
        #endregion



        #region MODULE_RELATED_COMMANDS
        [AutoServerCommand("moduletypes_list")]
        public IEnumerable<string> ModuleTypesList()
        {
            return DFlow.Module.AllModuleTypes.Select(x => x.Name);
        }

        [AutoServerCommand("module_load")]
        public string ModuleStart(string modulename,  object [] args=null, DFlowCore.Engine engine=null)
        {
            if (engine == null)
            {
                engine = this;
            }
            DFlow.Module m = (DFlow.Module)DFlow.Module.AllModuleTypes.Find(x => x.Name == modulename).InvokeMember(
                    null,
                    System.Reflection.BindingFlags.DeclaredOnly |
                    System.Reflection.BindingFlags.Public |
                    System.Reflection.BindingFlags.NonPublic |
                    System.Reflection.BindingFlags.Instance |
                    System.Reflection.BindingFlags.CreateInstance, null, null, new object[] { }
            );
            DFlowCore.Log.Info("Instantiated module " + modulename);
            m.Initialize(engine,args);
            m.Start();
            return "ok";
        }



        [AutoServerCommand("module_unload")]
        public string ModuleStop(string moduletypename, string moduleid = "")
        {
            foreach (DFlow.Module m in DFlow.Module.Instances.Where(x => x.GetType().Name == moduletypename))
            {
                m.Stop();
            }
            DFlow.Module.InstancesRemoveAll(x => x.GetType().Name == moduletypename);
            return "ok";
        }
        #endregion


    }
}